package service

import (
	"context"
	"errors"
	"log"
	"sync"
	"time"

	"github.com/ammyhaber/seckill/pkg/errcode"
	"github.com/ammyhaber/seckill/service/inventory/cache"
	"github.com/ammyhaber/seckill/service/inventory/dao"
	"github.com/ammyhaber/seckill/service/inventory/model"
)

type InventoryService struct {
	inventoryDAO *dao.InventoryDAO
	redisCache   *cache.RedisCache
	syncLock     sync.Mutex
}

func NewInventoryService(inventoryDAO *dao.InventoryDAO, redisCache *cache.RedisCache) *InventoryService {
	return &InventoryService{
		inventoryDAO: inventoryDAO,
		redisCache:   redisCache,
	}
}

// InitCache 初始化缓存，将MySQL中的库存数据预热到Redis
func (s *InventoryService) InitCache(ctx context.Context) error {
	// 从数据库获取所有库存信息
	details, err := s.inventoryDAO.GetInventoryDetailList()
	if err != nil {
		return err
	}

	// 转换为库存模型
	inventories := make([]*model.Inventory, 0, len(details))
	for _, detail := range details {
		inventories = append(inventories, &detail.Inventory)
	}

	// 预热到Redis
	return s.redisCache.WarmUpInventory(ctx, inventories)
}

// GetInventoryDetail 获取库存详情
func (s *InventoryService) GetInventoryDetail(ctx context.Context, productID int64) (*model.InventoryDetail, error) {
	// 先从Redis获取
	cacheInv, err := s.redisCache.GetInventory(ctx, productID)
	if err != nil || cacheInv == nil {
		// Redis获取失败或不存在，从数据库获取
		return s.getInventoryDetailFromDB(productID)
	}

	// 从数据库获取商品信息与基本库存数据
	detail, err := s.inventoryDAO.GetInventoryDetailByProductID(productID)
	if err != nil {
		return nil, err
	}
	if detail == nil {
		return nil, errors.New(errcode.GetMsg(errcode.ProductNotExist))
	}

	// 使用Redis中的实时库存数据
	detail.Available = cacheInv.Available
	detail.Locked = cacheInv.Locked
	detail.Version = cacheInv.Version
	return detail, nil
}

// getInventoryDetailFromDB 从数据库获取库存详情
func (s *InventoryService) getInventoryDetailFromDB(productID int64) (*model.InventoryDetail, error) {
	detail, err := s.inventoryDAO.GetInventoryDetailByProductID(productID)
	if err != nil {
		return nil, err
	}
	if detail == nil {
		return nil, errors.New(errcode.GetMsg(errcode.ProductNotExist))
	}
	return detail, nil
}

// DecrInventory 扣减库存
func (s *InventoryService) DecrInventory(ctx context.Context, productID int64, count int) (bool, error) {
	// 先从Redis扣减
	success, err := s.redisCache.DecrInventory(ctx, productID, count)
	if err != nil {
		// Redis操作失败，尝试从数据库扣减
		return s.decrInventoryFromDB(ctx, productID, count)
	}

	if !success {
		// Redis库存不足
		return false, errors.New(errcode.GetMsg(errcode.InventoryNotEnough))
	}

	// 异步同步到数据库
	go func() {
		if err := s.syncInventoryToDB(context.Background(), productID); err != nil {
			log.Printf("同步库存到数据库失败: %v\n", err)
		}
	}()

	return true, nil
}

// decrInventoryFromDB 从数据库扣减库存
func (s *InventoryService) decrInventoryFromDB(ctx context.Context, productID int64, count int) (bool, error) {
	// 获取锁
	lockValue, err := s.redisCache.AcquireLock(ctx, productID)
	if err != nil {
		return false, errors.New("获取锁失败，请稍后重试")
	}
	defer s.redisCache.ReleaseLock(ctx, productID, lockValue)

	// 获取当前库存
	inventory, err := s.inventoryDAO.GetByProductID(productID)
	if err != nil {
		return false, err
	}
	if inventory == nil {
		return false, errors.New(errcode.GetMsg(errcode.ProductNotExist))
	}

	// 检查库存是否足够
	if inventory.Available < count {
		return false, errors.New(errcode.GetMsg(errcode.InventoryNotEnough))
	}

	// 使用乐观锁更新库存
	success, err := s.inventoryDAO.DecrByProductID(productID, count, inventory.Version)
	if err != nil {
		return false, err
	}

	if !success {
		return false, errors.New(errcode.GetMsg(errcode.InventoryDecrFail))
	}

	// 更新Redis缓存
	inventory.Available -= count
	inventory.Locked += count
	inventory.Version += 1

	// 重新预热该商品的库存
	err = s.redisCache.WarmUpInventory(ctx, []*model.Inventory{inventory})
	if err != nil {
		log.Printf("更新Redis缓存失败: %v\n", err)
	}

	return true, nil
}

// ConfirmDeduction 确认扣减库存（订单支付成功后调用）
func (s *InventoryService) ConfirmDeduction(ctx context.Context, productID int64, count int) error {
	// 先更新Redis
	err := s.redisCache.ConfirmDeduction(ctx, productID, count)
	if err != nil {
		log.Printf("Redis确认扣减失败: %v\n", err)
	}

	// 更新数据库
	return s.inventoryDAO.ConfirmDeductionByProductID(productID, count)
}

// CancelDeduction 取消扣减（订单取消时调用）
func (s *InventoryService) CancelDeduction(ctx context.Context, productID int64, count int) error {
	// 先更新Redis
	err := s.redisCache.UnlockInventory(ctx, productID, count)
	if err != nil {
		log.Printf("Redis取消扣减失败: %v\n", err)
	}

	// 更新数据库
	return s.inventoryDAO.UnlockByProductID(productID, count)
}

// syncInventoryToDB 同步Redis库存到数据库
func (s *InventoryService) syncInventoryToDB(ctx context.Context, productID int64) error {
	// 加锁防止并发同步
	s.syncLock.Lock()
	defer s.syncLock.Unlock()

	// 从Redis获取最新库存
	cacheInv, err := s.redisCache.GetInventory(ctx, productID)
	if err != nil || cacheInv == nil {
		return errors.New("从Redis获取库存失败")
	}

	// 从数据库获取当前库存
	dbInv, err := s.inventoryDAO.GetByProductID(productID)
	if err != nil {
		return err
	}
	if dbInv == nil {
		return errors.New(errcode.GetMsg(errcode.ProductNotExist))
	}

	// 如果版本号不一致，更新数据库
	if cacheInv.Version != dbInv.Version {
		// 使用乐观锁更新
		// 更新数据库：确保总库存不变，只调整可用库存和锁定库存
		affected, err := s.inventoryDAO.ExecUpdateInventory(
			cacheInv.Available,
			cacheInv.Locked,
			int64(cacheInv.Version),
			productID,
			int64(dbInv.Version))
		if err != nil {
			return err
		}

		if affected == 0 {
			// 更新失败，可能是版本冲突
			return errors.New("同步库存失败，版本冲突")
		}
	}

	return nil
}

// StartInventorySyncTask 启动库存同步任务
func (s *InventoryService) StartInventorySyncTask(ctx context.Context) {
	// 定时同步所有商品库存
	go func() {
		ticker := time.NewTicker(5 * time.Minute)
		defer ticker.Stop()

		for {
			select {
			case <-ticker.C:
				s.syncAllInventory(ctx)
			case <-ctx.Done():
				return
			}
		}
	}()
}

// syncAllInventory 同步所有商品库存
func (s *InventoryService) syncAllInventory(ctx context.Context) {
	// 获取所有商品ID
	details, err := s.inventoryDAO.GetInventoryDetailList()
	if err != nil {
		log.Printf("获取库存列表失败: %v\n", err)
		return
	}

	// 逐个同步
	for _, detail := range details {
		err := s.syncInventoryToDB(ctx, detail.ProductID)
		if err != nil {
			log.Printf("同步商品[%d]库存失败: %v\n", detail.ProductID, err)
		}
	}

	log.Println("库存同步完成")
}
